package com.iwuyc.tools.commons.util.math;

import com.iwuyc.tools.commons.classtools.ClassUtils;
import com.iwuyc.tools.commons.util.collection.ArrayUtil;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Objects;

/**
 * 数字工具类
 *
 * @author Neil
 * @since 2017年10月15日
 */
public class NumberUtils {
    /**
     * less than
     */
    public static final int LESS_THAN = -1;
    /**
     * equals
     */
    public static final int EQUAL = 0;
    /**
     * greater than
     */
    public static final int GREATER_THAN = 1;
    /**
     * integer 的总“位”长度
     */
    public static final int INTEGER_BIT_LENGTH = 32;
    /**
     * long 的总“位”长度
     */
    public static final int LONG_BIT_LENGTH = 64;

    /**
     * short 的总“字节”长度
     */
    public static final int SHORT_BYTE_LENGTH = 2;
    /**
     * integer 的总“字节”长度
     */
    public static final int INTEGER_BYTE_LENGTH = 4;
    /**
     * long 的总“字节”长度
     */
    public static final int LONG_BYTE_LENGTH = 8;

    private NumberUtils() {
    }

    public static String toBit(int number) {
        return toBit(number, false);
    }

    public static String toBit(int number, boolean format) {
        return turnToBitChar(INTEGER_BIT_LENGTH, number, format);
    }

    public static String toBit(long number) {
        return toBit(number, false);
    }

    public static String toBit(long number, boolean format) {
        return turnToBitChar(LONG_BIT_LENGTH, number, format);
    }

    private static String turnToBitChar(int bitLength, long number, boolean format) {

        final StringBuilder bitBuilder = new StringBuilder();
        int counter = 1;
        for (int i = bitLength - 1; i >= 0; i--, counter++) {
            if ((number & 1) == 1) {
                bitBuilder.append('1');
            } else {
                bitBuilder.append('0');
            }
            if (counter == 8) {
                if (format) {
                    bitBuilder.append(' ');
                }
                counter = 0;
            }
            number = number >>> 1;

        }
        final int lastCharIndex = bitBuilder.length() - 1;
        if (lastCharIndex >= 0 && bitBuilder.charAt(lastCharIndex) == ' ') {
            bitBuilder.deleteCharAt(lastCharIndex);
        }
        return bitBuilder.reverse().toString();
    }

    public static boolean isByteClass(Class<?> target) {
        return Byte.class.equals(target) || byte.class.equals(target);
    }

    public static boolean isIntegerClass(Class<?> target) {
        return Integer.class.equals(target) || int.class.equals(target);
    }

    public static boolean isLongClass(Class<?> target) {
        return Long.class.equals(target) || long.class.equals(target);
    }

    public static boolean isFloatClass(Class<?> target) {
        return Float.class.equals(target) || float.class.equals(target);
    }

    public static boolean isDoubleClass(Class<?> target) {
        return Double.class.equals(target) || double.class.equals(target);
    }

    /**
     * 判断类是否为数字类型
     *
     * @param target 待判断的类对象
     * @return 如果是数字类型，则返回true，否则返回false。
     */
    public static boolean isNumberClass(Class<?> target) {
        if (null == target) {
            return false;
        }
        return Number.class.isAssignableFrom(target) || (target.isPrimitive() && target != Void.TYPE);
    }

    /**
     * 判断是否是数字。
     *
     * @param object 待判断的对象。
     * @return 如果是数字，则返回true，否则返回false。
     */
    public static boolean isNumber(Object object) {
        return isNumberClass(object.getClass());
    }

    /**
     * 判断字符串是否为整型
     *
     * @param numStr 待判断的字符串
     * @return 如果是整型则返回true，否则返回false
     */
    public static boolean isInteger(String numStr) {
        for (int i = 0; i < numStr.length(); i++) {
            final char item = numStr.charAt(i);
            if (item < '0' || item > '9') {
                return false;
            }
        }
        return true;
    }

    /**
     * 判断字符串是否为浮点类型
     *
     * @param numStr 待判断的字符串
     * @return 如果是整型则返回true，否则返回false
     */
    public static boolean isDouble(String numStr) {
        boolean hasPoint = false;
        for (int i = 0; i < numStr.length(); i++) {
            final char item = numStr.charAt(i);
            if (item == '.') {
                if (hasPoint) {
                    return false;
                } else {
                    hasPoint = true;
                }
            } else if (item < '0' || item > '9') {
                return false;
            }

        }
        return hasPoint;
    }

    /**
     * 判断字符串是否为数字
     *
     * @param numStr 待判断的字符串
     * @return 如果是整型则返回true，否则返回false
     */
    public static boolean isNumber(String numStr) {
        return isInteger(numStr) || isDouble(numStr);
    }

    /**
     * 将字符串类型转换成目标的数字类型
     *
     * @param numberFormat 待转换的数字字符串
     * @param target       目标类型
     * @param <T>          目标类型
     * @return 转换后的值
     */
    @SuppressWarnings("unchecked")
    public static <T extends Number> T parse(String numberFormat, Class<T> target) {
        Number result;
        if (isByteClass(target)) {
            result = Byte.parseByte(numberFormat);
        } else if (isIntegerClass(target)) {
            result = Integer.parseInt(numberFormat);
        } else if (isLongClass(target)) {
            result = Long.parseLong(numberFormat);
        } else if (isFloatClass(target)) {
            result = Float.parseFloat(numberFormat);
        } else if (isDoubleClass(target)) {
            result = Double.parseDouble(numberFormat);
        } else {
            // 如果是其他类型，则该类型必须要有String作为入参的构造函数。如：BigDecimal、BigInteger
            result = ClassUtils.instance(Number.class, target, numberFormat);
            if (null == result) {
                throw new UnsupportedOperationException("The target type unsupport.Target type:" + target);
            }
        }
        return (T) result;
    }

    public static Number parse(String val) {
        Number result;
        if (isInteger(val)) {
            result = parse(val, Integer.TYPE);
        } else {
            result = parse(val, Double.TYPE);
        }
        return result;
    }

    /**
     * 将字符串的int类型转换为数字类型
     *
     * @param intStr 待转换的字符串
     * @return 转换后的值
     */
    public static int parseInt(String intStr) {
        return parse(intStr, int.class);
    }

    /**
     * 比较两个数值，如果one小于other则返回true，否则返回false，null将会当成无限大处理
     *
     * @param one   第一个数值
     * @param other 第二个数值
     * @return 如果one小于other则返回true，否则返回false
     */
    public static boolean lt(Number one, Number other) {
        return compare(one, other, true) < 0;
    }

    /**
     * 比较两个数值，如果one大于等于other则返回true，否则返回false，null将会当成无限大处理
     *
     * @param one   第一个数值
     * @param other 第二个数值
     * @return 如果one大于等于other则返回true，否则返回false
     */
    public static boolean gte(Number one, Number other) {
        return !lt(one, other);
    }

    /**
     * 比较两个数值，如果one大于other则返回true，否则返回false，null将会当成无限小处理
     *
     * @param one   第一个数值
     * @param other 第二个数值
     * @return 如果one大于other则返回true，否则返回false
     */
    public static boolean gt(Number one, Number other) {
        return compare(one, other, false) > 0;
    }

    /**
     * 比较两个数值，如果one小于等于other则返回true，否则返回false，null将会当成无限小处理
     *
     * @param one   第一个数值
     * @param other 第二个数值
     * @return 如果one小于等于other则返回true，否则返回false
     */
    public static boolean lte(Number one, Number other) {
        return !gt(one, other);
    }

    /**
     * 比较两个数并返回较小的数，null当成无限大处理
     *
     * @param one   第一个数
     * @param other 另外一个数
     * @param <T>   数的真实类型
     * @return 较小的值
     */
    public static <T extends Number> T min(T one, T other) {
        return lt(one, other) ? one : other;
    }

    /**
     * 比较两个数并返回较大的数，null当成无限小处理
     *
     * @param one   第一个数
     * @param other 另外一个数
     * @param <T>   数的真实类型
     * @return 较大的值
     */
    public static <T extends Number> T max(T one, T other) {
        return gt(one, other) ? one : other;
    }

    /**
     * 比较两个值的大小
     *
     * @param one       第一个数字
     * @param other     第二个数字
     * @param nullIsMax 如果为true则将null值当成无限大，如果为false，则当成无限小
     * @return 如果one大于other，则返回1，如果相等，则返回0，如果one小于other，则返回-1
     */
    @SuppressWarnings("unchecked")
    private static int compare(Number one, Number other, boolean nullIsMax) {
        if (Objects.equals(one, other)) {
            return EQUAL;
        }
        if (one == null) {
            return nullIsMax ? GREATER_THAN : LESS_THAN;
        } else if (other == null) {
            return nullIsMax ? LESS_THAN : GREATER_THAN;
        }
        if (!(one instanceof Comparable && other instanceof Comparable)) {
            throw new IllegalArgumentException("one 与 other 必须实现comparable接口。one:" + one.getClass() + ";other:" + other.getClass());
        }
        return ((Comparable<Number>) one).compareTo(other);
    }

    /**
     * 将 BigDecimal 类型的数值转换为字节数组
     * <pre>
     * 规则：
     * new BigDecimal("1"); // byte[]{0, 0, 0, 0, 1}
     * new BigDecimal("10"); // byte[]{(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, 1}
     * new BigDecimal("100"); // byte[]{(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFE, 1}
     * new BigDecimal("0.01"); // byte[]{0, 0, 0, 2, 1}
     * </pre>
     * 至少5个字节，前面4个字节表示int scale，后续字节表示的是unscaledValue
     *
     * @param number 要转换的 BigDecimal 数值
     * @return 转换后的字节数组.如果number为null，则返回一个长度为0的byte数组
     * @throws IllegalArgumentException 如果number的scale超过了int的范围，则抛出异常
     */
    public static byte[] toBytes(BigDecimal number) {
        if (null == number) {
            return new byte[0];
        }
        final byte[] scale = toBytes(number.scale());
        final byte[] bytes = number.unscaledValue().toByteArray();

        final byte[] result = new byte[scale.length + bytes.length];
        System.arraycopy(scale, 0, result, 0, scale.length);
        System.arraycopy(bytes, 0, result, scale.length, bytes.length);
        return result;
    }

    /**
     * 将double类型的数字转换为byte数组
     *
     * @param number 待转换的数字
     * @return 转换后的byte数组
     * @author Neil
     * @since 2023-08-22
     */
    public static byte[] toBytes(double number) {
        final long doubleLongBits = Double.doubleToLongBits(number);
        return toBytes(doubleLongBits);
    }

    /**
     * 将float类型的数字转换为byte数组
     *
     * @param number 待转换的数字
     * @return 转换后的byte数组
     * @author Neil
     * @since 2023-08-22
     */
    public static byte[] toBytes(float number) {
        final int floatIntBits = Float.floatToIntBits(number);
        return toBytes(floatIntBits);
    }

    /**
     * 将BigInteger类型的数值转换为字节数组
     *
     * @param number 要转换的BigInteger数值
     * @return 转换后的字节数组
     */
    public static byte[] toBytes(BigInteger number) {
        if (null == number) {
            return new byte[0];
        }
        return number.toByteArray();
    }

    /**
     * 将long类型的数字转换为byte数组
     *
     * @param number 待转换的数字
     * @return 转换后的byte数组
     * @author Neil
     * @since 2023-08-22
     */
    public static byte[] toBytes(long number) {
        return toBytes(number, LONG_BYTE_LENGTH);
    }

    /**
     * 将int类型的数字转换为byte数组
     *
     * @param number 待转换的数字
     * @return 转换后的byte数组
     * @author Neil
     * @since 2023-08-22
     */
    public static byte[] toBytes(int number) {
        return toBytes(number, INTEGER_BYTE_LENGTH);
    }

    /**
     * 将short类型的数字转换为byte数组
     *
     * @param number 待转换的数字
     * @return 转换后的byte数组
     * @author Neil
     * @since 2023-08-22
     */
    public static byte[] toBytes(short number) {
        return toBytes(number, SHORT_BYTE_LENGTH);
    }

    /**
     * 将short类型的数字转换为byte数组
     *
     * @param bytes 待转换的数字
     * @return 转换后的byte数组
     * @author Neil
     * @since 2023-08-22
     */
    public static byte[] toBytes(byte... bytes) {
        return bytes;
    }

    /**
     * 将short类型的数字转换为byte数组
     *
     * @param booleanVal 待转换的布尔值
     * @return 转换后的byte数组
     * @author Neil
     * @since 2023-08-22
     */
    public static byte[] toBytes(boolean booleanVal) {
        return booleanVal ? new byte[]{1} : new byte[]{0};
    }

    /**
     * 将一个长整型数字转换为指定字节长度的字节数组
     *
     * @param number     需要转换的长整型数字
     * @param byteLength 需要转换成的字节长度
     * @return 转换后的字节数组
     */
    private static byte[] toBytes(long number, int byteLength) {
        final byte[] bytes = new byte[byteLength];
        long innerNumber = number;
        for (int i = byteLength - 1; i >= 0; i--) {
            final byte current = (byte) (innerNumber & 0xFF);
            innerNumber = innerNumber >>> 8;
            bytes[i] = current;
        }
        return bytes;
    }


    /**
     * 将byte数组转换为float类型的数据，bytes最多4个，如果超过4个，则从右往左舍弃，例如：new byte[]{4,3,2,1,0}，此数组，相当于new byte[]{3,2,1,0}，4会被忽略
     *
     * @param bytes 待处理的字节数组
     * @return 转换后的值
     */
    public static float toFloat(byte[] bytes) {
        return Float.intBitsToFloat(toInt(bytes));
    }

    /**
     * 将byte数组转换为double类型的数据，bytes最多4个，如果超过4个，则从右往左舍弃，例如：new byte[]{4,3,2,1,0}，此数组，相当于new byte[]{3,2,1,0}，4会被忽略
     *
     * @param bytes 待处理的字节数组
     * @return 转换后的值
     */
    public static double toDouble(byte[] bytes) {
        return Double.longBitsToDouble(toLong(bytes));
    }

    /**
     * 将字节数组转换为BigDecimal对象。需要符合 {@link NumberUtils#toBytes(BigDecimal)} 的规则。
     *
     * @param bytes 字节数组
     * @return 转换后的BigDecimal对象
     */
    public static BigDecimal toBigDecimal(byte[] bytes) {
        // 如果字节数组为空或长度小于5，则返回0
        if (ArrayUtil.isEmpty(bytes) || bytes.length < 5) {
            return BigDecimal.ZERO;
        }

        // 从字节数组的前4个字节获取小数的刻度值
        final int scale = toInt(Arrays.copyOfRange(bytes, 0, 4));

        // 从字节数组中获取未刻度化的值
        final byte[] unscaledValue = Arrays.copyOfRange(bytes, 4, bytes.length);

        // 使用未刻度化的值和刻度值创建BigDecimal对象
        return new BigDecimal(new BigInteger(unscaledValue), scale);
    }


    /**
     * 将byte数组转换为long类型的数据，bytes最多8个，如果超过8个，则从右往左舍弃，例如：new byte[]{9,8,7,6,5,4,3,2,1,0}，此数组，相当于new byte[]{7,6,5,4,3,2,1,0}，9跟8会被忽略
     *
     * @param bytes 待处理的字节数组
     * @return 转换后的值
     */
    public static long toLong(byte[] bytes) {
        if (ArrayUtil.isEmpty(bytes)) {
            return 0;
        }
        return toNumber(bytes, LONG_BYTE_LENGTH);
    }

    /**
     * 将字节数组转换为 BigInteger 对象
     *
     * @param bytes 字节数组
     * @return 转换后的 BigInteger 对象
     */
    public static BigInteger toBigInteger(byte[] bytes) {
        if (ArrayUtil.isEmpty(bytes)) {
            return BigInteger.ZERO;
        }
        return new BigInteger(bytes);
    }


    /**
     * 将byte数组转换为int类型的数据，bytes最多4个，如果超过4个，则从右往左舍弃，例如：new byte[]{9,8,7,6,5,4,3,2,1,0}，此数组，相当于new byte[]{3,2,1,0}，其他都会被忽略
     *
     * @param bytes 待处理的字节数组
     * @return 转换后的值
     */
    public static int toInt(byte[] bytes) {
        if (ArrayUtil.isEmpty(bytes)) {
            return 0;
        }
        return (int) toNumber(bytes, INTEGER_BYTE_LENGTH);
    }

    /**
     * 将byte数组转换为short类型的数据，bytes最多4个，如果超过4个，则从右往左舍弃，例如：new byte[]{9,8,7,6,5,4,3,2,1,0}，此数组，相当于new byte[]{3,2,1,0}，其他都会被忽略
     *
     * @param bytes 待处理的字节数组
     * @return 转换后的值
     */
    public static short toShort(byte[] bytes) {
        if (ArrayUtil.isEmpty(bytes)) {
            return 0;
        }
        return (short) toNumber(bytes, SHORT_BYTE_LENGTH);
    }


    private static long toNumber(byte[] bytes, int byteLength) {
        final int startIndex = max(0, bytes.length - byteLength);
        long result = 0;
        for (int i = startIndex; i < bytes.length; i++) {
            result = result << 8;
            result = (bytes[i] & 0xFF) | result;
        }
        return result;
    }

}
